嗨,我是Hogan
目前在經營自己的自媒體 hogan.tech
主要分享一些有關於程式碼、軟體和科技業經驗分享
有興趣的讀者可以進一步關注我,進而獲得更多資訊唷!
未來文章一併更新於此網站 Hogan.B Lab
並且包含多語系 繁體中文、英文、日文、簡體中文
觀看分類:React 白話文運動、其他系列
如果想要快速查找其他文章請點選目錄
成立 hogan.tech 的初衷是
希望每個正在這條路上探索的人,
都可以透過 Hogan.tech 嘗試進入程式領域。
前一篇介紹與useMemo 相似的Hook - useCallback
這一篇會介紹另一個與useEffect 很像的Hook - useLayoutEffect
這邊可以先來看一下這個元件,如果直接執行這個元件,
function Component() {
console.log("render components");
useEffect(() => {
console.log("useEffect trigger")
})
return (
<>
{console.log("render JSX")}
</>
)
}
如果呼叫上面的元件,會依照順序印出三行的結果
分別為
理由是因為載入元件後會先執行裡面的函式
Component元件第二行會先被執行,印出 render components
接下來遇到 useEffect 則會擺到渲染後去執行
然後再回傳 JSX 時,執行裡面的程式,印出 render JSX
最後回傳完畢後再去執行useEffect裡面的函式,印出 useEffect trigger
這樣的執行順序也與元件的生命週期有關
以上面的流程來說
那如果我們修改元件的狀態,元件因此重新渲染呢?
除了渲染元件的步驟以外,其實也會重新執行 useEffect 裡面的函式
import React from 'react'
import { useState } from 'react';
import { useEffect } from 'react';
export default function EffectComponent() {
console.log("render components");
const [value, setValue] = useState(0)
useEffect(() => {
console.log("useEffect trigger")
console.log(value)
}, [value])
return (
<>
<button onClick={() => setValue(value + 1)}></button>
{console.log("render JSX")}
</>
)
}
這邊另外搭配了 useState 給予元件一個狀態叫做value,點擊按鈕後可以讓 value 加一
如果實際執行程式,並且點擊按鈕後,會分別印出以下
不過這邊也要跟讀者強調一下,如果不熟悉 useEffect 的讀者,可能一不小心就會陷入無窮渲染
import React from 'react'
import { useState } from 'react';
import { useEffect } from 'react';
export default function EffectComponent() {
console.log("render components");
const [value, setValue] = useState(0)
useEffect(() => {
console.log(value)
setData()
})
const setData = () => {
setValue(value + 1)
}
return (
<>
<button onClick={setData}></button>
{console.log("render JSX")}
</>
)
}
如果直接執行以上的程式,會看到 console 一直跑,並且數字一直往上,為什麼呢?
理由是因為,以上面的流程來說
就會進入無窮渲染
首先要講解一下 useLayoutEffect 的觸發時機
這邊也快速給予一個 useEffect 以及 useLayoutEffect 的例子:
import React, { useEffect, useLayoutEffect } from 'react';
function App(){
useEffect(()=>console.log("useEffect render"));
useLayoutEffect(()=>console.log("useLayoutEffect render"));
return <></>
}
即便 useEffect 寫在 useLayoutEffect 前面,但是實際執行起來
useLayoutEffect 會是先印出訊息,才接著是 useEffect 印出訊息
那麼實際在哪些情況會做使用呢?
架設現在有一種情況是想要透過當前視窗大小,來去繪圖
那麼應該有兩件事情需要執行:
那問題來了,如果直接在useEffect 去執行這兩件事
顯然如果重新渲染,會一直重複去做獲得當前視窗大小
但如果直接將它放在useLayoutEffect 就不用擔心這個問題
import React, { useLayoutEffect, useState } from 'react';
function useMousePosition() {
const [x, setX] = useState(0);
const [y, setY] = useState(0);
const setPosition = ({ x, y }) => {
setX(x);
setY(y);
}
useLayoutEffect(() => {
window.addEventListener("mousemove", setPosition);
return () => window.removeEventListener("mousemove", setPosition);
}, [])
return <></>
}
接下來就是一些文字的提醒了,在使用React Hook 的時候,常常遇到莫名的錯誤
所以有一些原則如果注意了,就可以大幅減少錯誤或是異常
目前我有寫到的範例程式應該不難發現,我都是在React元件中去做使用
一個很大的理由是 Hook 本身就是 React 的程式,而非JavaScript 內建的語法
所以如果直接擺到一般純 JavaScript 的語法中,一定會出錯
當系統越來越複雜,並且大量使用各種Hook 的時候,將功能拆成不同的 Hook 是相當重要的
以我個人工作經驗,我們團隊會將資料使用Store 自定義的Hook來去資料做控管,並且使用 Custom Hook 來去處理邏輯
通常我們在使用Hook時,不應該放進判別式或是迴圈之中
const [count, setCount] = useState(0);
if(count > 5){
useEffect(()=>{
console.log(count);
setCount(count+1);
},[count])
}
以這上面的例子來說,如果在判別式之中放入 useEffect,代表這個 Hook 只有在count > 5時才會被執行
這會讓React 在比對Hook時的陣列時,因為長度不同而產生錯誤的行為
這篇講解了另一個與useEffect 很相似的Hook,除了講解 useLayoutEffect 的使用方法以外
也另外提到一些使用Hook應該注意的原則
如果有任何建議與疑問也歡迎留言!
如果喜歡此系列文章,請不吝於按下喜歡及分享,讓更多人看到唷~